/// 闭包 (Closure)通常是指词法闭包 ，是一个持有外部环境变量的函数。外部环境是指闭
/// 包定义时所在的词法作用域。外部环境变量，在函数式编程范式中也被称为自由变量 ，
/// 是指并不是在闭包内定义的变量。将自由变量和自身绑定的函数就是闭包
/// 其实在 Rust 中，闭包是一种语法糖 。 也就是说，闭包不属于 Rust 语言提供的基本语法要素，
/// 而是在基本语法功能之上又提供的一层方便开发者编程的语法。闭包和普通函数的差别就是闭包可以
/// 捕获环境中的自由变量。
pub fn start() {
    let f = counter(32);
    println!("{}", f(12));

    let closure = Closure { env_var: 3 };
    // let i = closure.call();
    println!("{}", closure.env_var);

    let mut x = 0;
    let incr_x = || x += 1;
    call(incr_x);
    // call(incr_x);  编译报错 value used here after move，因为&mut引用没有实现Copy属性

    // 这次不会报错，因为 x 已经被移动进闭包当中（i32是Copy的类型，那么相当于Copy到了闭包当中）
    // 所以闭包实现了Copy语义
    // 闭包在使用 move 关键字的时候，如果捕获变量是复制语义类型的，则闭包会自动实现 Copy/Clone;
    // 如果捕获变量是移动语义类型的，则闭包不会自动实现 Copy/Clone，这也是出于保证内存安全的考虑
    let incr_x = move || x += 1;
    call(incr_x);
    call(incr_x);

    //对移动语义类型使用 move
    let mut x = vec![];
    let expend_x = move || x.push(1);
    call(expend_x);
    // 编译报错： closure cannot be moved more than once as it is not `Copy` due
    // to moving the variable `x` out of its environment
    // 因为x是移动语义，第一次调用的时候，已经移动了
    // call(expend_x);

    let mut s = "rust".to_string();
    let mut c = || s += " rust";
    c();
    c();
    println!("{}", s);

    let s = "hello".to_string();
    let core = move || println!("{}", s);
    test(core);
}

fn test<F>(f: F)
where
    F: Fn() + 'static,
{
    f();
}

fn counter(i: i32) -> impl Fn(i32) -> i32 {
    move |n| i + n
}

fn call<F: FnOnce()>(f: F) {
    f();
}

/// 模拟编译器对闭包的实现
struct Closure {
    env_var: u32,
}

// 只能在Nightly环境下使用，必须注解特性![feature(unboxed_closures, fn_traits)]
// 所以注释掉下面的代码
// impl FnOnce<()> for Closure {
//     type Output = u32;
//
//     extern "rust-call" fn call_once(self, args: ()) -> Self::Output {
//         println!("call it FnOnce ()");
//         self.env_var + 2
//     }
// }
//
// impl FnMut<()> for Closure {
//     extern "rust-call" fn call_mut(&mut self, args: ()) -> Self::Output {
//         println!("call it FnMut ()");
//         self.env_var + 2
//     }
// }
//
// impl Fn<()> for Closure {
//     extern "rust-call" fn call(&self, args: ()) -> Self::Output {
//         println!("call it Fn ()");
//         self.env_var + 2
//     }
// }
